package pig

import (
	"context"
	"net/http"
	"regexp"
	"strings"

	"github.com/gorilla-go/pig/constant"
)

const (
	WEB_SYSTEM_SEPARATOR = "/"
)

type Router struct {
	routes       map[string][]*SubRouter
	lenIndex     map[int][]string
	snippetCache map[string][]*PathSnippet
	miss         func(ctx context.Context)
}

type PathSnippet struct {
	paramName       string
	paramMode       bool
	pathSnippetName string
	regexp          string
}

type SubRouter struct {
	method     constant.HttpMethod
	controller func(ctx context.Context)
}

func NewRouter() *Router {
	return &Router{
		routes:       make(map[string][]*SubRouter),
		lenIndex:     make(map[int][]string),
		snippetCache: make(map[string][]*PathSnippet),
		miss:         nil,
	}
}

// 路径格式化
func (r *Router) pathFormatter(path string) string {
	path = strings.TrimSpace(path)
	return strings.TrimLeft(path, WEB_SYSTEM_SEPARATOR)
}

// 添加路由
func (r *Router) addRoute(path string, httpMethod constant.HttpMethod, controller func(ctx context.Context)) *Router {
	if _, ok := r.routes[path]; !ok {
		r.routes[path] = []*SubRouter{}
	}
	path = r.pathFormatter(path)

	if httpMethod != constant.HttpMethod("ANY") {
		r.routes[path] = append([]*SubRouter{{
			method:     httpMethod,
			controller: controller,
		}}, r.routes[path]...)
	} else {
		r.routes[path] = append(r.routes[path], &SubRouter{
			method:     httpMethod,
			controller: controller,
		})
	}

	pathSnippets := strings.Split(path, WEB_SYSTEM_SEPARATOR)
	for _, snippet := range pathSnippets {
		paramMode := false
		paramName := ""
		regexpStr := ""
		reg := regexp.MustCompile(`^:([a-zA-Z0-9_-]+)|<([a-zA-Z0-9_-]+):\s+?(\S+)>$`)
		if reg.MatchString(snippet) {
			subMatchedArr := reg.FindStringSubmatch(snippet)
			clearSubMatchedArr := []string{}
			for _, subMatched := range subMatchedArr {
				if len(subMatched) == 0 {
					continue
				}
				clearSubMatchedArr = append(clearSubMatchedArr, subMatched)
			}
			paramName = clearSubMatchedArr[1]
			if len(clearSubMatchedArr) == 3 {
				regexpStr = clearSubMatchedArr[2]
			}
			paramMode = true
		}
		r.snippetCache[path] = append(r.snippetCache[path], &PathSnippet{
			paramName:       paramName,
			paramMode:       paramMode,
			pathSnippetName: snippet,
			regexp:          regexpStr,
		})
	}

	length := len(pathSnippets)
	r.lenIndex[length] = append(r.lenIndex[length], path)
	return r
}

// 添加GET路由
func (r *Router) Get(path string, controller func(ctx context.Context)) *Router {
	r.addRoute(path, constant.GET, controller)
	return r
}

// 添加POST路由
func (r *Router) Post(path string, controller func(ctx context.Context)) *Router {
	r.addRoute(path, constant.POST, controller)
	return r
}

// 添加PUT路由
func (r *Router) Put(path string, controller func(ctx context.Context)) *Router {
	r.addRoute(path, constant.PUT, controller)
	return r
}

// 添加DELETE路由
func (r *Router) Delete(path string, controller func(ctx context.Context)) *Router {
	r.addRoute(path, constant.DELETE, controller)
	return r
}

// 添加ANY路由
func (r *Router) Any(path string, controller func(ctx context.Context)) *Router {
	r.addRoute(path, constant.HttpMethod("ANY"), controller)
	return r
}

// 设置404路由
func (r *Router) Miss(controller func(ctx context.Context)) *Router {
	r.miss = controller
	return r
}

// 路由
func (r *Router) Route(request *http.Request) (controller func(ctx context.Context), params map[string]string) {
	defer func() {
		if controller == nil && r.miss != nil {
			controller = r.miss
		}
	}()

	path := r.pathFormatter(request.URL.Path)
	pathSnippets := strings.Split(path, WEB_SYSTEM_SEPARATOR)
	length := len(pathSnippets)
	params = make(map[string]string)
	if _, ok := r.lenIndex[length]; !ok {
		return
	}

	for _, path := range r.lenIndex[length] {
	outLoop:
		for _, subRouter := range r.routes[path] {
			if subRouter.method == constant.HttpMethod(request.Method) ||
				subRouter.method == constant.HttpMethod("ANY") {
				for index, snippet := range r.snippetCache[path] {
					if snippet.paramMode && len(pathSnippets[index]) > 0 {
						if snippet.regexp != "" {
							reg := regexp.MustCompile(snippet.regexp)
							if !reg.MatchString(pathSnippets[index]) {
								clear(params)
								break outLoop
							}
							params[snippet.paramName] = reg.FindString(pathSnippets[index])
							continue
						}
						params[snippet.paramName] = pathSnippets[index]
						continue
					}

					if snippet.pathSnippetName != pathSnippets[index] {
						clear(params)
						break outLoop
					}
				}
				return subRouter.controller, params
			}
		}
	}
	return
}
